App.require(["matrix3"], function (matrix3) {
    var Matrix3 = matrix3.Matrix3;
    App.defineWidget("pie", {
        render: function (ctx) {
            ctx.save();
            var style = this.style;
            if (style.option.length == 0) return;
            ctx.globalAlpha = style.opacity;
            var shadowObj = App.Widget.strToShadow(style.shadow);
            if (shadowObj) {
                ctx.shadowOffsetX = shadowObj.x;
                ctx.shadowOffsetY = shadowObj.y;
                ctx.shadowBlur = shadowObj.blur;
                ctx.shadowColor = shadowObj.color;
            }
            var transforms = App.Widget.strToTransform(style.transform);
            if (transforms) {
                for (var i = 0; i < transforms.length; i++) {
                    var tranObj = transforms[i];
                    ctx[tranObj.key].apply(ctx, tranObj.param);
                }
            }
            var ratioList = this.computeRatio();
            ctx.translate(style.x, style.y);
            for (var i = 0; i < ratioList.length; i++) {
                var ratioObj = ratioList[i];
                var option = style.option[i];
                this.createPath(ctx, ratioObj, option);
                var fillStyle = option.fill || style.fill,
                    strokeStyle = option.stroke || style.stroke;
                if (fillStyle) {
                    ctx.fillStyle = fillStyle;
                    ctx.fill();
                }
                if (strokeStyle) {
                    ctx.strokeStyle = strokeStyle;
                    ctx.stroke();
                }
            }
            ctx.restore();
        },
        isPointIn: function (ctx, x, y) {
            var style = this.style;
            ctx.beginPath();
            ctx.arc(style.x, style.y, style.or, 0, Math.PI * 2, false);
            ctx.arc(style.x, style.y, style.ir, 0, Math.PI * 2, true);
            return ctx.isPointInPath(x, y);
        },
        createPath: function (ctx, ratioObj, option, ratio) {
            var style = this.style, ratio = ratio || 1;
            var offset = ratioObj.offset * style.max,
            duration = ratioObj.current * style.max;

            var start = style.startoffset + (style.direction == "CW" ? offset : -offset);
            var end = start + (style.direction == "CW" ? duration : -duration);
            var rx = 0, ry = 0;
            if (option.offset) {
                var middle = (start + end) / 2;
                rx = Math.cos(middle) * option.offset * ratio;
                ry = Math.sin(middle) * option.offset * ratio;
            }
            
            ctx.beginPath();
            var ir = parseFloat(option.ir || style.ir), or = parseFloat(option.or || style.or);
            if (style.ir) {
                ctx.moveTo(rx + or * Math.cos(start) * ratio, ry + or * Math.sin(start) * ratio);
                ctx.arc(rx, ry, or * ratio, start, end, style.direction !== "CW");
                ctx.lineTo(rx + ir * Math.cos(end) * ratio, ry + ir * Math.sin(end) * ratio);
                ctx.arc(rx, ry, ir * ratio, end, start, style.direction === "CW");
            }
            else {
                ctx.moveTo(rx, ry);
                ctx.lineTo(rx + or * Math.cos(start) * ratio, ry + or * Math.sin(start) * ratio);
                ctx.arc(rx, ry, or * ratio, start, end, style.direction !== "CW");
            }
            
            ctx.closePath();

        },
        computeRatio: function () {
            var sum = 0, offsets = [], options = this.style.option;
            for (var i = 0; i < options.length; i++) {
                offsets.push(sum);
                sum += parseFloat(options[i].value);
            }
            var ratioList = [];
            for (var i = 0; i < offsets.length; i++) {
                ratioList.push({
                    offset: offsets[i] / sum,
                    current: parseFloat(options[i].value) / sum
                });
            }
            return ratioList;
        },
        style: {
            transform: [], 
            or: 100,
            ir: 30,
            x: 0,
            y: 0,
            direction: App.PieDirectionOption.CCW,
            startoffset: -Math.PI / 2,
            max: Math.PI * 2,
            shadow: "0 0 0 black",
            stroke: "black",
            fill: "red",
            mark: "",
            marker: "",
            markOffset: "",
            markerOffset: "",
            opacity: 1,
            linewidth: 1,
            linedash: "",
            linedashoffset: 0,
            option: [ { value: 20 }, { value: 40 } ]
        },
        get transform() {
            return this.style.transform;
        },
        set transform(val) {
            this.style.transform = val;
            this.getScene().nextRender();
        },
        get x() {
            return this.style.x;
        },
        set x(val) {
            this.style.x = val;
            this.getScene().nextRender();
        },
        get y() {
            return this.style.y;
        },
        set y(val) {
            this.style.y = val;
            this.getScene().nextRender();
        },
        get direction() {
            return this.style.direction;
        },
        set direction(val) {
            this.style.direction = val;
            this.getScene().nextRender();
        },
        get startoffset() {
            return this.style.startoffset;
        },
        set startoffset(val) {
            this.style.startoffset = val;
            this.getScene().nextRender();
        },
        get or() {
            return this.style.or;
        },
        set or(val) {
            this.style.or = val;
            this.getScene().nextRender();
        },
        get ir() {
            return this.style.ir;
        },
        set ir(val) {
            this.style.ir = val;
            this.getScene().nextRender();
        },
        get mark() {
            return this.style.mark;
        },
        set mark(val) {
            this.style.mark = val;
            this.getScene().nextRender();
        },
        get markOffset() {
            return this.style.markOffset;
        },
        set markOffset(val) {
            this.style.markOffset = val;
            this.getScene().nextRender();
        },
        get marker() {
            return this.style.marker;
        },
        set marker(val) {
            this.style.marker = val;
            this.getScene().nextRender();
        },
        get markerOffset() {
            return this.style.markerOffset;
        },
        set markerOffset(val) {
            this.style.markerOffset = val;
            this.getScene().nextRender();
        },
        get shadow() {
            return this.style.shadow;
        },
        set shadow(val) {
            this.style.shadow = val;
            this.getScene().nextRender();
        },
        get stroke() {
            return this.style.stroke;
        },
        set stroke(val) {
            this.style.stroke = val;
            this.getScene().nextRender();
        },
        get fill() {
            return this.style.fill;
        },
        set fill(val) {
            this.style.fill = val;
            this.getScene().nextRender();
        },
        get opacity() {
            return this.style.opacity;
        },
        set opacity(val) {
            this.style.opacity = val;
            this.getScene().nextRender();
        },
        get linewidth() {
            return this.style.linewidth;
        },
        set linewidth(val) {
            this.style.linewidth = val;
            this.getScene().nextRender();
        },
        get linedash() {
            return this.style.linedash;
        },
        set linedash(val) {
            this.style.linedash = val;
            this.getScene().nextRender();
        },
        get linedashoffset() {
            return this.style.linedashoffset;
        },
        set linedashoffset(val) {
            this.style.linedashoffset = val;
            this.getScene().nextRender();
        },
        get max() {
            return this.style.max;
        },
        set max(val) {
            this.style.max = val;
            this.getScene().nextRender();
        }
    })
})